<template>
  <div
    :class="[...classes, ...cascaderClasses]"
    v-click-outside:[capture]="onClickOutside"
    v-click-outside:[capture].mousedown="onClickOutside"
    v-click-outside:[capture].touchstart="onClickOutside"
  >
    <div
      ref="reference"
      :class="selectionCls"
      :tabindex="selectTabindex"
      @blur="toggleHeaderFocus"
      @focus="toggleHeaderFocus"
      @click="toggleMenu"
      @keydown.esc="handleKeydown"
      @keydown.enter="handleKeydown"
      @keydown.up.prevent="handleKeydown"
      @keydown.down.prevent="handleKeydown"
      @keydown.tab="handleKeydown"
      @keydown.delete="handleKeydown"
      @mouseenter="hasMouseHoverHead = true"
      @mouseleave="hasMouseHoverHead = false"
    >
      <slot name="input">
        <input type="hidden" :name="name" :value="publicValue" />
        <select-head
          :filterable="!showFilter && filterable"
          :multiple="multiple"
          :values="values"
          :clearable="canBeCleared"
          :prefix="prefix"
          :disabled="itemDisabled"
          :remote="remote"
          :input-element-id="elementId"
          :initial-label="initialLabel"
          :placeholder="placeholder"
          :query-prop="query"
          :max-tag-count="maxTagCount"
          :max-tag-placeholder="maxTagPlaceholder"
          :allow-create="allowCreate"
          :show-create-item="showCreateItem"
          @on-query-change="onQueryChange"
          @on-input-focus="isFocused = true"
          @on-input-blur="isFocused = false"
          @on-clear="clearSingleSelect"
          @on-enter="handleCreateItem"
        >
          <slot name="prefix"></slot>
          <span v-if="isLimited">{{ value.length }}/{{ limited }}</span>
        </select-head>
      </slot>
    </div>
    <transition name="transition-drop">
      <Drop
        ref="dropdown"
        :classes="dropdownCls"
        :visible="dropVisible"
        :placement="placement"
        :eventsEnabled="eventsEnabled"
        :transfer="transfer"
        transition-name="transition-drop"
      >
        <Caspanel
          :check-strictly="checkStrictly"
          :options="displayOptions"
          :trigger="trigger"
          @on-change="onChange"
        />
        <ul
          v-show="!this.options || !this.options.length"
          :class="['ivu-cascader' + '-not-found-tip']"
        >
          <li>{{ localeNotFoundText }}</li>
        </ul>
      </Drop>
    </transition>
  </div>
</template>
<script>
import {
  computed,
  defineComponent,
  getCurrentInstance,
  reactive,
  ref,
  nextTick,
  watch,
  onMounted,
  provide,
  onBeforeUnmount,
  withModifiers,
  resolveComponent,
} from 'vue'
import Select from '../select/select.vue'
import Caspanel from './Caspanel.vue'
import Drop from '../select/dropdown.vue'
import { oneOf } from '../../utils/assist'
import mitt from 'mitt'
const emitter = mitt()

const prefixCls = 'haloe-cascader'
const selectPrefixCls = 'haloe-select'

export default defineComponent({
  extends: Select,
  // eslint-disable-next-line
  name: 'iSelect',
  components: {
    Caspanel,
    Drop,
  },
  props: {
    notFoundText: {
      type: String,
    },
    multiple: {
      type: Boolean,
      default: true,
    },
    options: {
      type: Array,
      default: () => [],
    },
    splitor: {
      type: String,
      default: '/',
    },
    checkStrictly: {
      type: Boolean,
      default: true,
    },
    trigger: {
      validator(value) {
        return oneOf(value, ['click', 'hover'])
      },
      default: 'click',
    },
    showCheckedStrategy: {
      validator(value) {
        return oneOf(value, ['SHOW_CHILD', 'SHOW_ALL', 'SHOW_PARENT'])
      },
      default: 'SHOW_CHILD',
    },
    /**
     * [
     *   [
     *     { label, value, checked }, {label, value, checked}, {label, value, checked}
     *   ],
     *   [
     *     { label, value, checked }, {label, value, checked}, {label, value, checked}
     *   ]
     * ]
     */
  },
  setup(props) {
    const instance = getCurrentInstance()
    console.log("---", instance.proxy);
    const initOptions = function () {
      return props.options.map((node) => {
        let n = calcOptions(node)
        // this.$set(n, 'parent', null)
        n.parent = null
        return n
      })
    }
    const onChange = function (changeItem, flag = false) {
      let isChecked = flag || !changeItem.checked
      // 子节点
      setChildren(changeItem, isChecked)
      // 前溯
      props.checkStrictly && setIndeterminate(changeItem, isChecked)

      const paths = optionsToPaths(displayOptions.value)
      instance.proxy.values = paths.map((path) => ({
        label: path.map((opt) => opt.label).join(props.splitor),
        value: path.map((opt) => opt.value).join(props.splitor),
      }))
      instance.proxy.$emit('on-change-object', instance.proxy.values)
    }
    const optionsToPaths = function (options) {
      // Options tree to List<Path>
      // Path: List<Option>
      const paths = []
      const path = []
      const rec = (opt) => {
        const isLeaf = !opt.children || opt.children.length === 0
        path.push(opt)

        // 输出条件
        if (props.showCheckedStrategy === 'SHOW_ALL') {
          if (opt.checked) {
            paths.push([...path])
          }
        } else if (props.showCheckedStrategy === 'SHOW_PARENT') {
          if (opt.checked) {
            paths.push([...path])
            path.pop()
            return
          }
        } else {
          if (isLeaf && opt.checked) {
            paths.push([...path])
          }
        }

        if (!isLeaf) {
          opt.children.forEach(rec)
        }
        path.pop()
      }
      options.forEach(rec)
      return paths
    }
    const calcOptions = function (node) {
      if (node.children && node.children.length) {
        node.children.forEach((item) => {
          calcOptions(item)
          // this.$set(item, 'parent', node)
          item.parent = node
        })
      }
      /* this.$set(node, 'checked', false)
      this.$set(node, 'active', false)
      this.$set(node, 'indeterminate', false)
      this.$set(node, 'selected', false) */
      node = {
        ...node,
        checked: false,
        active: false,
        indeterminate: false,
        selected: false,
      }
      return node
    }
    const removeTag = function (v, flag) {
      const valArray = v.value.split(props.splitor)
      const changeItem = valArray.reduce((opt, v, index) => {
        const ret = opt && opt.find((o) => v === String(o.value))
        if (index === valArray.length - 1) {
          return ret
        } else {
          return (ret || {}).children
        }
      }, displayOptions.value)
      if (changeItem) {
        onChange(changeItem)
      }
    }
    const selectTag = function (v) {
      removeTag(v, true)
    }
    const setChildren = function (item, isChecked, parent) {
      item.checked = isChecked
      console.log("set child", item.label, isChecked, "parent, ", parent?.label, " checked: ", parent?.checked)
      item.indeterminate = false
      console.log("before set child", displayOptions.value)
      if (item.children && item.children.length) {
        item.children.forEach((child) => setChildren(child, isChecked, item))
      }
      console.log("after set children", displayOptions.value)
      // instance.proxy.$forceUpdate()
    }
    const setIndeterminate = function (item, isChecked) {
      if (item.parent) {
        if (isChecked) {
          if (
            item.parent.children.find(
              (child) => !child.checked || child.indeterminate
            )
          ) {
            item.parent.indeterminate = true
            item.parent.checked = false
          } else {
            item.parent.indeterminate = false
            item.parent.checked = true
          }
        } else {
          if (
            item.parent.children.find(
              (child) => child.checked || child.indeterminate
            )
          ) {
            item.parent.indeterminate = true
            item.parent.checked = false
          } else {
            item.parent.indeterminate = false
            item.parent.checked = false
          }
        }
        setIndeterminate(item.parent, isChecked)
      }
    }
    const reset = function () {
      instance.proxy.query = ''
      instance.proxy.focusIndex = -1
      instance.proxy.unchangedQuery = true
      instance.proxy.values = []
      instance.proxy.filterQueryChange = false
      // 清空值，并重置下拉框中的选项
      initOptions()
      instance.proxy.$emit('on-change-object', instance.proxy.values)
    }
    const displayOptions = computed(() => {
      const ret = initOptions()
      nextTick(() => {
        ;(instance.proxy.value || []).forEach((value) => selectTag({ value }))
      })
      return ret
    })
    return {
      initOptions,
      onChange,
      optionsToPaths,
      calcOptions,
      removeTag,
      selectTag,
      setChildren,
      setIndeterminate,
      reset,
      displayOptions,
    }
  },
  computed: {
    /* displayOptions() {
      // TODO: 拷贝值
      const ret = this.initOptions()
      this.$nextTick(() => {
        ;(this.value || []).forEach((value) => this.selectTag({ value }))
      })
      return ret
    }, */
    canBeCleared() {
      const uiStateMatch = this.hasMouseHoverHead || this.active
      const qualifiesForClear = !this.itemDisabled && this.clearable
      return uiStateMatch && qualifiesForClear && this.reset // we return a function
    },
    dropdownCls() {
      return {
        [prefixCls + '-transfer']: this.transfer,
        [this.transferClassName]: this.transferClassName,
      }
    },
    cascaderClasses() {
      return [
        `${prefixCls}`,
        {
          [`${prefixCls}-show-clear`]: this.showCloseIcon,
          [`${prefixCls}-size-${this.size}`]: !!this.size,
          [`${prefixCls}-visible`]: this.visible,
          [`${prefixCls}-disabled`]: this.itemDisabled,
          [`${prefixCls}-not-found`]:
            this.filterable &&
            this.query !== '' &&
            !this.querySelections.length,
        },
      ]
    },
  },
  watch: {
    value(val) {
      // 不一样才要变
      if (
        this.values.length !== val.length ||
        (val || []).find((v) => !this.values.find((item) => item.value === v))
      ) {
        this.$nextTick(() => {
          this.initOptions()
          return (val || []).forEach((value) => this.selectTag({ value }))
        })
      }
    },
  },
  methods: {
    /* initOptions() {
      return this.options.map((node) => {
        let n = this.calcOptions(node)
        // this.$set(n, 'parent', null)
        n.parent = null
        return n
      })
    },
    onChange(changeItem, flag = false) {
      let isChecked = flag || !changeItem.checked
      // 子节点
      this.setChildren(changeItem, isChecked)
      // 前溯
      this.checkStrictly && this.setIndeterminate(changeItem, isChecked)

      const paths = this.optionsToPaths(this.displayOptions)
      this.values = paths.map((path) => ({
        label: path.map((opt) => opt.label).join(this.splitor),
        value: path.map((opt) => opt.value).join(this.splitor),
      }))
      this.$emit('on-change-object', this.values)
    },
    optionsToPaths(options) {
      // Options tree to List<Path>
      // Path: List<Option>
      const paths = []
      const path = []
      const rec = (opt) => {
        const isLeaf = !opt.children || opt.children.length === 0
        path.push(opt)

        // 输出条件
        if (this.showCheckedStrategy === 'SHOW_ALL') {
          if (opt.checked) {
            paths.push([...path])
          }
        } else if (this.showCheckedStrategy === 'SHOW_PARENT') {
          if (opt.checked) {
            paths.push([...path])
            path.pop()
            return
          }
        } else {
          if (isLeaf && opt.checked) {
            paths.push([...path])
          }
        }

        if (!isLeaf) {
          opt.children.forEach(rec)
        }
        path.pop()
      }
      options.forEach(rec)
      return paths
    },
    calcOptions(node) {
      if (node.children && node.children.length) {
        node.children.forEach((item) => {
          this.calcOptions(item)
          // this.$set(item, 'parent', node)
          item.parent = node
        })
      }
      /* this.$set(node, 'checked', false)
      this.$set(node, 'active', false)
      this.$set(node, 'indeterminate', false)
      this.$set(node, 'selected', false) */
    /* node = {
        ...node,
        checked: false,
        active: false,
        indeterminate: false,
        selected: false,
      }
      return node
    },
    removeTag(v, flag) {
      const valArray = v.value.split(this.splitor)
      const changeItem = valArray.reduce((opt, v, index) => {
        const ret = opt && opt.find((o) => v === String(o.value))
        if (index === valArray.length - 1) {
          return ret
        } else {
          return (ret || {}).children
        }
      }, this.displayOptions)
      if (changeItem) {
        this.onChange(changeItem)
      }
    },
    selectTag(v) {
      this.removeTag(v, true)
    },
    setChildren(item, isChecked) {
      item.checked = isChecked
      item.indeterminate = false
      if (item.children && item.children.length) {
        item.children.forEach((child) => this.setChildren(child, isChecked))
      }
      this.$foreceUpdate()
    },
    setIndeterminate(item, isChecked) {
      if (item.parent) {
        if (isChecked) {
          if (
            item.parent.children.find(
              (child) => !child.checked || child.indeterminate
            )
          ) {
            item.parent.indeterminate = true
            item.parent.checked = false
          } else {
            item.parent.indeterminate = false
            item.parent.checked = true
          }
        } else {
          if (
            item.parent.children.find(
              (child) => child.checked || child.indeterminate
            )
          ) {
            item.parent.indeterminate = true
            item.parent.checked = false
          } else {
            item.parent.indeterminate = false
            item.parent.checked = false
          }
        }
        this.setIndeterminate(item.parent, isChecked)
      }
    },
    reset() {
      this.query = ''
      this.focusIndex = -1
      this.unchangedQuery = true
      this.values = []
      this.filterQueryChange = false
      // 清空值，并重置下拉框中的选项
      this.initOptions()
      this.$emit('on-change-object', this.values)
    }, */
  },
  mounted() {
    emitter.on('on-select-selected', this.removeTag)
  },
})
</script>
